﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Reflection;
using System.Collections;
using ProgNetComponents.Design.Attributes;
using System.ComponentModel.Design;

namespace ProgNetComponents.Design
{
    public class PropertyInfoDescriptor : PropertyDescriptor
    {
        public static PropertyDescriptorCollection GetProperties(object target)
        {
            PropertyDescriptorCollection oryginal = TypeDescriptor.GetProperties(target, true);

            PropertyInfo[] propsInfo = null;
            List<PropertyInfo> pList = new List<PropertyInfo>();
            Type t = target.GetType();
            List<string> strList = new List<string>();
            
            List<PropertyDescriptor> list = new List<PropertyDescriptor>();
            Attribute attribute = null;
            while (t != typeof(Object) && t!=null)
            {
                propsInfo = t.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                pList.Clear();
                pList.AddRange(propsInfo);

                foreach (PropertyInfo prop in pList)
                {
                    if (!strList.Contains(prop.Name))
                    {
                        strList.Add(prop.Name);
                        ArrayList attributeList = new ArrayList();
                        bool changedAttribute = false;
                        foreach (Attribute attrib in prop.GetCustomAttributes(true))
                        {
                            if (attrib is IDynamicAttribute)
                            {
                                attribute = ((IDynamicAttribute)attrib).GetRealAttribute(target);
                                if (attribute != null)
                                {
                                    changedAttribute = true;
                                    attributeList.Add(attribute);
                                }
                            }
                            else
                                attributeList.Add(attrib);
                        }
                        if (changedAttribute)
                            list.Add(new PropertyInfoDescriptor(prop, (Attribute[])attributeList.ToArray(typeof(Attribute))));
                    }
                }
                t = t.BaseType;
            }
            bool found = false;
            for (int i = 0; i < oryginal.Count; i++)
            {
                found = false;
                if (oryginal[i].Name != "Name")
                {
                    for (int j = 0; j < list.Count; j++)
                    {
                        if (list[j].Name == oryginal[i].Name)
                        {
                            found = true;
                            break;
                        }
                    }
                }
                if (!found)
                    list.Add(oryginal[i]);
            }
            
            return new PropertyDescriptorCollection(list.ToArray());
        }
        
        public static IComponentChangeService GetChangeService(object component)
        {
            ISite site = MemberDescriptor.GetSite(component);
            System.ComponentModel.Design.IComponentChangeService service = null;
            if (site != null)
            {
                service = (System.ComponentModel.Design.IComponentChangeService)site.GetService(typeof(System.ComponentModel.Design.IComponentChangeService));
            }
            return service;
        }

        protected override void FillAttributes(IList attributeList)
        {
            base.FillAttributes(attributeList);
        }
        private PropertyInfo _Info;

        public PropertyInfoDescriptor(PropertyInfo info, Attribute[] attribs)
            : base(info.Name, attribs)
        {
            _Info = info;
        }
        
        private object DefaultValue
        {
            get
            {
                if (_Info.IsDefined(typeof(DefaultValueAttribute), false))
                {
                    return ((DefaultValueAttribute)_Info.GetCustomAttributes(typeof(DefaultValueAttribute), false)[0]).Value;
                }
                return PropertyType.IsValueType ? Activator.CreateInstance(PropertyType) : null;
            }
        }

        public override bool IsReadOnly
        {
            get
            {
                    return true;
                return this.Attributes.Contains(ReadOnlyAttribute.Yes) ||
                    this.Attributes.Contains(BrowsableAttribute.No);
            }
        }

        public override bool IsBrowsable
        {
            get
            {
                if (this.Attributes.Contains(BrowsableAttribute.No))
                    return false;
                return true;
            }
        }

        public override object GetValue(object component)
        {
            return _Info.GetValue(component, null);
        }
         
        public override bool CanResetValue(object component)
        {
            return ((!this.Attributes.Contains(ReadOnlyAttribute.Yes))
                & (this.DefaultValue != null && !this.DefaultValue.Equals(this.GetValue(component))));
        }

        public override void ResetValue(object component)
        {
            this.SetValue(component, this.DefaultValue);
        }

        public override void SetValue(object component, object value)
        {
            if (component != null)
            {
                System.ComponentModel.Design.IComponentChangeService service = GetChangeService(component);
                object oldValue = null;
                object invocationTarget = this.GetInvocationTarget(_Info.ReflectedType, component);
                if (!this.Attributes.Contains(ReadOnlyAttribute.Yes) || ShouldSerializeValue(component))
                {
                    if (service != null)
                    {
                        oldValue = invocationTarget.GetPropertyValue(_Info.Name);
                        try
                        {
                            service.OnComponentChanging(component, this);
                        }
                        catch (System.ComponentModel.Design.CheckoutException exception)
                        {
                            if (exception != System.ComponentModel.Design.CheckoutException.Canceled)
                            {
                                throw exception;
                            }
                            return;
                        }
                    }
                    try
                    {
                        invocationTarget.SetPropertyValue(_Info.Name, value);
                        this.OnValueChanged(invocationTarget, EventArgs.Empty);
                    }
                    catch (Exception exception2)
                    {
                        value = oldValue;
                        if ((exception2 is TargetInvocationException) && (exception2.InnerException != null))
                        {
                            throw exception2.InnerException;
                        }
                        throw exception2;
                    }
                    finally
                    {
                        if (service != null)
                        {
                            service.OnComponentChanged(component, this, oldValue, value);
                        }
                    }
                }
            }
        }

        public override bool ShouldSerializeValue(object component)
        {
            if (this.Attributes.Contains(SerializeAlwaysAttribute.Yes))
                return true;
            if (this.IsReadOnly)
                return false;
            if (PropertyType.IsValueType && _Info.GetValue(component) == null)
                return false;
            if (DefaultValue.IsTheSame(_Info.GetValue(component)))
                return false;
            return true;
        }

        public override Type ComponentType
        {
            get
            {
                return _Info.DeclaringType;
            }
        }

        public override Type PropertyType
        {
            get
            {
                return _Info.PropertyType;
            }
        }

        
    }
}
